/*
 * @Author: DaTree
 * @Date: 2021-09-06 14:12:06
 * @LastEditTime: 2021-09-07 08:46:41
 * @LastEditors: DaTree
 * @Description: DaTreeEdit
 * @FilePath: \demo_services\static\lib\clientXHRSignalingChannel.js
 * 天将降大任于是人也，必先苦其心志，劳其筋骨，饿其体肤，空乏其身，行拂乱其所为。 ——《孟子•告子下》
 * Copyright 2021 DaTree
 */

/**
 * 次代码将创建客户端命令，用于简历XML HTTP请求的WebRTC信令通道
 * 此信令通道假定通过共享密钥建立双人连接。
 * 每次连接尝试都会导致状态在“waiting”和“connected”之间切换，
 * 这意味着如果有两个浏览器已简历连接。
 * 而另一个浏览器尝试进行连接，将断开现有连接，并且新浏览器的状态将变为“waiting”
 */

var createSignalingChannel = function (key, handlers) {
  var id, status, doNothing = function () { },
    handlers = handlers || {},
    initHandler = function (h) {
      return (typeof h === 'function' && h) || doNothing;
    },
    waitingHandler = initHandler(handlers.onWaiting),
    connectedHandler = initHandler(handlers.onConnected),
    messageHandler = initHandler(handlers.onMessage);

  function connect (failureCB) {
    var failureCB = (typeof h === 'function' && h) || function () { }
    function handler () {
      if (this.readyState == this.DONE) {
        if (this.status == 200 && this.response != null) {
          var res = JSON.parse(this.response)
          if (res.err) {
            failureCB("error " + res.err);
            return;
          }
          // 如果没有错误，则保存状态和服务器生成的id，
          // 然后启动异步消息轮询。
          id = res.id;
          status = res.status;
          poll();
          //运行用户提供的处理程序来处理waiting和connected状态
          if (status === 'waiting') {
            waitingHandler();
          } else {
            connectedHandler();
          }
          return;
        } else {
          failureCB("HTTP error:" + this.status);
          return;
        }
      }
    }

    //打开XHR 并发送包含密钥的连接请求。
    var client = new XMLHttpRequest();
    client.onreadystatechange = handler;
    client.open('GET', "/connect?key=" + key);
    client.send();
  }
  // poll() 会再次访问服务器之前等待n毫秒。
  // 对于前10次尝试，n为10毫秒；对于接下来的10次尝试，n为100毫秒；对于后续尝试n均为1000毫秒。
  // 如果实际收到消息，则将n重置为10毫秒。
  function poll () {
    var pollWaitDelay = (function () {
      var delay = 10, counter = 1;
      function reset () {
        delay = 10;
        counter = 1;
      }
      function increase () {
        counter += 1;
        if (counter > 20) {
          delay = 1000;
        } else if (counter > 10) {
          delay = 100;
        }
      }
      function value () {
        return delay;
      }
      return {
        reset, increase, value
      }
    })();
    // 此处立即定义并使用了getLoop
    // 它从服务器中检索消息，然后将自身计划在为 poolWaitDelay.value()毫秒后重新运行。
    (function getLoop () {
      get(function (response) {
        var i, msgs = (response && response.msgs) || [];
        // 如果存在消息属性，则表示我们已建立连接
        if (response.msgs && (status !== 'connected')) {
          // 将状态切换为connected,因为现在确实已建立连接！
          status = "connected";
          connectedHandler();
        }
        if (msgs.length > 0) {
          pollWaitDelay.reset();
          for (i = 0; i < msgs.length; i++) {
            handleMessage(msgs[i]);
          }
        } else {
          pollWaitDelay.increase();
        }
        // 现在设置计时器以便重新检查
        setTimeout(getLoop, pollWaitDelay.value());
      })
    })();
    // 此函数是轮询设置的一部分，该设置用于检查是否有来自另一端浏览器的消息。
    // 它由poll()中的getLoop()调用。
    function get (getResponseHandler) {
      // 响应应为错误或者JSON对象
      // 如果是后者，则将其发送给用户提供的处理程序。
      function handler () {
        if (this.readyState == this.DONE) {
          if (this.status == 200 && this.response != null) {
            var res = JSON.parse(this.response);
            if (res.err) {
              getResponseHandler("error: " + res.err);
              return;
            }
            getResponseHandler(res);
            return res;
          } else {
            getResponseHandler("HTTP error:" + this.status);
            return;
          }
        }
      }
      // 打开XHR并针对我的ID请求消息
      var client = new XMLHttpRequest();
      client.onreadystatechange = handler;
      client.open("POST", "/get");
      client.send(JSON.stringify({ id }))
    }
    // 计划传入的消息以进行一步处理
    // 此函数由poll()中的getLoop使用。
    function handleMessage (msg) {
      // 异步处理消息
      setTimeout(function () {
        messageHandler(msg)
      }, 0);
    }

  }
  // 通过信令通道向另一端的浏览器发送消息
  function send (msg, responseHandler) {
    var responseHandler = responseHandler || function () { };
    // 分析响应并发送给处理程序
    function handler () {
      if (this.readyState == this.DONE) {
        if (this.status == 200 && this.response != null) {
          var res = JSON.parse(this.response)
          if (res.err) {
            responseHandler("error： " + res.err);
            return
          }
          responseHandler(res);
          return
        }
      } else {
        responseHandler("HTTP error: " + this.status);
        return;
      }
    }
    // xhr
    var client = new XMLHttpRequest();
    client.onreadystatechange = handler;
    client.open("POST", "/send");
    var sendData = { id, "message": msg };
    client.send(JSON.stringify(sendData));
  }
  return {
    connect,
    send
  }
}
